#include "esp_log.h"
#include "esp_https_ota.h"
#include "esp_err.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/event_groups.h"
#include "Drivers/tcp_drv.h"
#include "cJSON.h"
#include "App/ota_task.h"
#include "App/sn_handle.h"
#include "Drivers/uart_drv.h"
#include "App/uart_data.h"
#include "App/http_client_task.h"

#define TEST_URL "http://192.168.31.254:8000/chfs/shared/Cook2.0.bin"

#define TAG "OTA"

EventGroupHandle_t OTAEventGroup = NULL;            // OTA事件组

bool isMyUpdate = true;                 //是否是自身升级
bool GetVersionSuccess = false;         //获取版本成功

#define FILE_ASK "FILES_UPDATE_CHECK {\"model\":\"pmk-wifi\",\
\"version\":\"0.0.5\",\
\"sn\":\"3C504D30000539\"}\n"               //测试

static void StcUpdateStart(void);

/**
 * @brief 连接到OTA升级服务器并发送版本检查请求
 *
 * 该函数根据升级类型(自身升级或主控升级)执行以下操作:
 * 1. 建立TCP连接(仅自身升级时)
 * 2. 获取设备型号和当前版本信息
 * 3. 构建JSON格式的版本检查请求
 * 4. 发送请求到服务器
 *
 * @return esp_err_t
 *         - ESP_OK: 连接成功并发送请求
 *         - 其他: 连接失败或发送失败
 *
 * @note 失败时会设置OTA_ERROR_BIT事件标志
 */
static esp_err_t ConnectToServer(void)
{
    char model[MODEL_MAX_LEN];
    char version[VERSION_MAX_LEN]; 
    esp_err_t err;

    if(isMyUpdate)      
    {       
        err = TCP_Connect();
        strcpy(model, MODEL);
        strcpy(version, LOCAL_VERSION);

        if (err != ESP_OK)
        {
            xEventGroupSetBits(OTAEventGroup, OTA_ERROR_BIT);       //连接失败，自身升级和主控升级都不继续
            return err;
        }
    }
    else
    {
        strcpy(model, MODEL_STC);
        uint16_t ver_Hex = GetSTCVersion();         //获取版本
        if(ver_Hex == 0)
            strcpy(version, NULL_VERSION_STC);      //获取版本失败，使用空版本
        else
            sprintf(version,"0.0.%04d",ver_Hex);
    }

    // char s[] = FILE_ASK;
    char str[100] = {0};
    char *str_sn = (char*)GetSNStr();

    cJSON *pRoot = cJSON_CreateObject();
    cJSON_AddStringToObject(pRoot,"model", model);
    cJSON_AddStringToObject(pRoot,"version", version);
    cJSON_AddStringToObject(pRoot,"sn", str_sn);
    char *sendData = cJSON_PrintUnformatted(pRoot);
    sprintf(str, "FILES_UPDATE_CHECK %s\n", sendData);

    err = TCP_Send((uint8_t *)str, strlen(str));
    printf("%s\n",str);

    if (err != ESP_OK)          //发送失败   
        xEventGroupSetBits(OTAEventGroup, OTA_ERROR_BIT);
    
    str[0] = '\0';

    cJSON_free(sendData);
    cJSON_Delete(pRoot);
    return err;
}

/**
 * @brief 自身升级事件处理
 */
static void event_handler(void* arg, esp_event_base_t event_base,
    int32_t event_id, void* event_data)
{
    if (event_base == ESP_HTTPS_OTA_EVENT) 
    {
        switch (event_id) {
        case ESP_HTTPS_OTA_START:
        ESP_LOGI(TAG, "OTA started");
        break;
        case ESP_HTTPS_OTA_CONNECTED:
        ESP_LOGI(TAG, "Connected to server");
        break;
        case ESP_HTTPS_OTA_GET_IMG_DESC:
        ESP_LOGI(TAG, "Reading Image Description");
        break;
        case ESP_HTTPS_OTA_VERIFY_CHIP_ID:
        ESP_LOGI(TAG, "Verifying chip id of new image: %d", *(esp_chip_id_t *)event_data);
        break;
        case ESP_HTTPS_OTA_DECRYPT_CB:
        ESP_LOGI(TAG, "Callback to decrypt function");
        break;
        case ESP_HTTPS_OTA_WRITE_FLASH:
        ESP_LOGD(TAG, "Writing to flash: %d written", *(int *)event_data);
        break;
        case ESP_HTTPS_OTA_UPDATE_BOOT_PARTITION:
        ESP_LOGI(TAG, "Boot partition updated. Next Partition: %d", *(esp_partition_subtype_t *)event_data);
        break;
        case ESP_HTTPS_OTA_FINISH:
        ESP_LOGI(TAG, "OTA finish");
        break;
        case ESP_HTTPS_OTA_ABORT:
        ESP_LOGI(TAG, "OTA abort");
        break;
        }
    }
}

/**
 * @brief 执行固件升级操作
 *
 * 该函数从指定URL下载并安装新的固件。执行过程如下:
 * 1. 获取升级固件的URL地址
 * 2. 配置HTTP客户端参数
 * 3. 执行OTA升级
 * 4. 升级成功后重启设备
 *
 * @return
 *     - ESP_OK: 升级成功
 *     - ESP_FAIL: 升级失败(URL无效或升级过程出错)
 */
esp_err_t do_firmware_upgrade()
{
    char *g_url = (char *)GetUrl();
    ESP_LOGI(TAG,"%s",g_url);
    if(g_url == NULL)   return ESP_FAIL;

    esp_http_client_config_t config = {
        .url = g_url,
        // .cert_pem = (char *)server_cert_pem_start,
    };
    esp_https_ota_config_t ota_config = {
        .http_config = &config,
    };
    esp_err_t ret = esp_https_ota(&ota_config);
    if (ret == ESP_OK) {
        esp_restart();
    } else {
        ESP_LOGE(TAG, "Firmware upgrade failed");
        
        return ESP_FAIL;
    }
    return ESP_OK;
}


/**
 * @brief OTA升级任务函数
 *
 * 该任务负责处理固件OTA升级流程,包括:
 * - 创建OTA事件组和事件循环
 * - 连接升级服务器
 * - 检测并执行ESP32固件升级
 * - 检测并执行STC单片机固件升级
 * - 通过事件标志位控制整个升级流程
 *
 * @param param 任务参数(未使用)
 * @return void
 *
 * @note 任务会一直运行直到升级完成或出错时自动退出
 */
void OTA_Task(void *param)
{
    ESP_LOGI(TAG, "http_ota_task start");
    OTAEventGroup = xEventGroupCreate();            //创建ota事件组
    esp_event_loop_create_default();                //事件循环
    esp_event_handler_register(ESP_HTTPS_OTA_EVENT, ESP_EVENT_ANY_ID, &event_handler, NULL);        //注册事件处理函数

    ConnectToServer();                                  //连接服务器

    xEventGroupSetBits(OTAEventGroup, OTA_START_BIT);   //启动升级检测
    while (1)
    {
        EventBits_t uxBits =  xEventGroupWaitBits(OTAEventGroup, OTA_START_BIT | OTA_FINISH_BIT |
                                                                 OTA_ERROR_BIT | OTA_STC_START_BIT | 
                                                                 OTA_STC_FINISH_BIT | OTA_STC_DOWLOAD_BIT, 
                                                                true, false, portMAX_DELAY);    //等待标志位
        if (uxBits & OTA_START_BIT)
        {
            ESP_LOGI(TAG,"OTA_START");
            if(xSemaphoreTake(xSemTcpRecvNone, 500 / portTICK_PERIOD_MS))       //等待服务器返回是否有新版本
                xEventGroupSetBits(OTAEventGroup, OTA_FINISH_BIT);
            else
            {
                if(do_firmware_upgrade() == ESP_FAIL)
                    xEventGroupSetBits(OTAEventGroup, OTA_FINISH_BIT);    
            }    
        }
    
        if(uxBits & OTA_FINISH_BIT)             //升级失败或不需要升级，进入主控升级
        {
            ESP_LOGI(TAG,"OTA_FINISH");
            xEventGroupSetBits(OTAEventGroup, OTA_STC_START_BIT);
  
        }
        if(uxBits & OTA_ERROR_BIT)              //服务器连接失败，直接退出
        {
            ESP_LOGE(TAG,"OTA_ERROR");
            break;                              
        }
        if(uxBits & OTA_STC_START_BIT)          //stc升级开始
        {
            ESP_LOGI(TAG,"STC_OTA_START");
            isMyUpdate = false;
            StcUpdateStart();                   

        }  
        if(uxBits & OTA_STC_DOWLOAD_BIT)        //下载bin固件
        {
            ESP_LOGI(TAG,"OTA_DOWLOAD_BIN");

            if(xSemaphoreTake(xSemTcpRecvNone, 500 / portTICK_PERIOD_MS))   //不需要升级
                xEventGroupSetBits(OTAEventGroup, OTA_STC_FINISH_BIT);
            else
                xTaskCreate(DownloadFileTask, "download_file_task", 1024 * 8, NULL, 7, NULL);   //启动下载任务

        }       
        if(uxBits & OTA_STC_FINISH_BIT)
        {
            ESP_LOGI(TAG,"STC_OTA_FINISH");
            xEventGroupSetBits(OTAEventGroup, OTA_EXIT_BIT);
            break;
        }
        vTaskDelay(20 / portTICK_PERIOD_MS);       
    }
    ESP_LOGI(TAG, "OTA task exit");
    vTaskDelete(NULL);
}

/**
 * @brief 启动STC单片机固件更新流程
 *
 * 该函数实现单片机固件更新的状态机控制：
 * 1. 首先尝试获取单片机当前版本号(最多尝试5次)
 * 2. 检查版本号获取是否成功
 * 3. 连接服务器并开始固件下载
 *
 * 状态机包含以下状态:
 * - GET_VERSION: 发送获取版本号命令
 * - CHECK_VERSION: 检查版本号获取结果
 * - GET_BIN: 连接服务器并开始下载
 *
 * @note 函数通过事件组通知下载状态:
 * - OTA_STC_FINISH_BIT: 更新结束
 * - OTA_STC_DOWLOAD_BIT: 开始下载
 */
static void StcUpdateStart(void)
{
    char GetVersion[] = {0x01, 0x03, 0x00, 0x1d, 0x00, 0x01, 0x14, 0x0c};
    char status = GET_VERSION;
    int Count = 0;
    while (1)
    {
        switch (status)
        {
        case GET_VERSION:

            // ESP_LOGI(TAG, "GET_VERSION");
            if(Count < 5)               //尝试5次获取版本
            {
                uart_write_bytes(UART_DATA_NUM, GetVersion, sizeof(GetVersion));
                status = CHECK_VERSION;
            }
            else            
                status = GET_BIN;            
            Count++;
            break;
        
        case CHECK_VERSION:

            // ESP_LOGI(TAG, "CHECK_VERSION");
            if(GetVersionSuccess)
                status = GET_BIN;
            else
                status = GET_VERSION;
            break;

        case GET_BIN:

            ESP_LOGI(TAG, "GET_BIN");
            
            if(ConnectToServer() != ESP_OK)                                 //连接服务器失败
            {
                xEventGroupSetBits(OTAEventGroup, OTA_STC_FINISH_BIT);
                xEventGroupClearBits(OTAEventGroup, OTA_STC_DOWLOAD_BIT);
            }
            xEventGroupSetBits(OTAEventGroup, OTA_STC_DOWLOAD_BIT);         //启动下载
            return;             //无论下载成功与否，都退出

        default:
            break;
        }

        vTaskDelay(1000 / portTICK_PERIOD_MS);
    }
}